package com.mtw.bbs.reply.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mtw.bbs.common.core.security.UserDetails;
import com.mtw.bbs.common.core.security.context.SecurityContextHolder;
import com.mtw.bbs.constant.ReplyConstant;
import com.mtw.bbs.constant.ReplyLevelEnum;
import com.mtw.bbs.like.service.LikeProviderService;
import com.mtw.bbs.reply.mapper.ReplyContentMapper;
import com.mtw.bbs.reply.mapper.ReplyIndexMapper;
import com.mtw.bbs.reply.mapper.ReplyInfoMapper;
import com.mtw.bbs.reply.pojo.dto.ReplyDto;
import com.mtw.bbs.reply.pojo.form.ReplyForm;
import com.mtw.bbs.reply.pojo.vo.ReplyAdminVo;
import com.mtw.bbs.reply.pojo.vo.ReplyVo;
import com.mtw.bbs.util.ReplyUtil;
import com.mtw.bbs.common.core.constant.BusinessObjectType;
import com.mtw.bbs.common.core.constant.SysConstant;
import com.mtw.bbs.common.core.enums.SortEnum;
import com.mtw.bbs.common.core.exception.BusinessException;
import com.mtw.bbs.common.core.util.BeanCopyUtils;
import com.mtw.bbs.common.core.vo.BaseFileVo;
import com.mtw.bbs.common.core.vo.Result;
import com.mtw.bbs.article.pojo.vo.LikeCountVo;
import com.mtw.bbs.reply.pojo.entity.*;
import com.mtw.bbs.reply.service.ReplyFileService;
import com.mtw.bbs.reply.service.ReplyIndexService;
import com.mtw.bbs.reply.service.ReplySubjectService;
import com.mtw.bbs.userAdmin.feign.UserClient;
import com.mtw.bbs.userAdmin.pojo.dto.UserDto;
import com.mtw.bbs.userAdmin.pojo.vo.UserInfoVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 评论服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ReplyIndexServiceImpl extends ServiceImpl<ReplyIndexMapper, ReplyIndex> implements ReplyIndexService {

    private final UserClient userClient;
    private final RedissonClient redissonClient;
    private final ReplyInfoMapper replyInfoMapper;
    private final ReplyIndexMapper replyIndexMapper;
    private final ReplyFileService replyFileService;
    private final ThreadPoolTaskExecutor asyncExecutor;
    private final ReplyContentMapper replyContentMapper;
    private final ReplySubjectService replySubjectService;
    private final LikeProviderService likeProviderService;
    private final ThreadPoolTaskExecutor bizExecutor;


    /**
     * 分页获取评论
     *
     * @param page         当前页
     * @param size         分页大小
     * @param qw           查询条件
     * @return             分页结果
     */
    @Override
    public Page<ReplyAdminVo> getReplyAdminVoPage(int page, int size, QueryWrapper<ReplyIndex> qw){


        Page<ReplyDto> dtoPage = replyIndexMapper.getDtoPage(new Page<>(page, size), qw);
        Page<ReplyAdminVo> voPage = new Page<>(page,size,dtoPage.getTotal());
        voPage.setRecords(new ArrayList<>(size));
        if (CollectionUtil.isNotEmpty(dtoPage.getRecords())){

            Set<String> uidSet = new HashSet<>(size);
            List<String> ids = new ArrayList<>(size);
            dtoPage.getRecords().forEach(x->{
                uidSet.add(x.getCreatedBy());
                ids.add(x.getId());
            });

            // 查用户
            Map<String, UserInfoVo> userMap = ReplyUtil.getUserInfoVoMap4R(userClient.getUserInfoByIds(uidSet.toArray(String[]::new)));

            // 查附件
            Map<String, List<BaseFileVo>> fileMap = replyFileService.getFileMap(ids);

            dtoPage.getRecords().forEach(x->{
                ReplyAdminVo vo = BeanCopyUtils.copyBean(x, ReplyAdminVo.class);
                // 附件
                vo.setFileList(fileMap.get(vo.getId()));
                // 评论人信息
                vo.setPoster(userMap.get(x.getCreatedBy()));
                voPage.getRecords().add(vo);
            });
        }
        return voPage;
    }




    /**
     * 分页获评论
     *
     * @param qw           查询条件
     * @param sortType     排序方式，1为按点赞数，2为按时间排序
     * @param page         当前页
     * @param size         分页大小
     */
    public Page<ReplyVo> getReplyVoPage(QueryWrapper<ReplyIndex> qw,Integer sortType,int page, int size){
        return getReplyVoPageWithSon(qw,sortType,page,size,ReplyLevelEnum.REPLY_LEVEL_ONE,0);
    }



    /**
     * 分页获评论,并带出子评论
     *
     * @param qw           查询条件
     * @param sortType     排序方式，1为按点赞数，2为按时间排序
     * @param page         当前页
     * @param size         分页大小
     * @param level        当前评论等级
     * @param sonSize      子评论数量
     * @return             分页结果
     */
    @Override
    public Page<ReplyVo> getReplyVoPageWithSon(QueryWrapper<ReplyIndex> qw, Integer sortType,
                                               int page, int size, ReplyLevelEnum level, int sonSize) {

        // 当前用户
        UserDetails details = SecurityContextHolder.getContext().getAuthentication();
        String userId = details != null ? details.getId(): null;

        // 查询数据库
        Page<ReplyDto> dtoPage = replyIndexMapper.getDtoPage(new Page<>(page, size), qw);

        Page<ReplyVo> voPage = new Page<>(page,size,dtoPage.getTotal());
        voPage.setRecords(new ArrayList<>(size));
        if (CollectionUtil.isNotEmpty(dtoPage.getRecords())){

            Set<String> uidSet = new HashSet<>(size);
            List<String> ids = new ArrayList<>(size);
            dtoPage.getRecords().forEach(x->{
                uidSet.add(x.getCreatedBy());
                ids.add(x.getId());
                if (StringUtils.isNotBlank(x.getRepostedUserId())){
                    uidSet.add(x.getRepostedUserId());
                }
                ReplyVo vo = BeanCopyUtils.copyBean(x, ReplyVo.class);
                voPage.getRecords().add(vo);
            });

            // 解决跨线程获取不到request对象
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();


            // 获取子评论
            CompletableFuture<Void> sonF = CompletableFuture.supplyAsync(() -> {
                // 获取子评论
                if (sonSize > 0){
                    RequestContextHolder.setRequestAttributes(requestAttributes, true);
                    voPage.getRecords().forEach(x -> {
                        // 子评论查询条件
                        QueryWrapper<ReplyIndex> sqw = null;
                        switch (level) {
                            case REPLY_LEVEL_TWO -> sqw = getSonQueryWrapper(x.getId(), sortType);
                            case REPLY_LEVEL_THREE -> sqw = getLowSonQueryWrapper(x.getId(), sortType);
                            default -> sqw = getQueryWrapper(x.getId(), sortType);
                        }
                        // 获取子评论
                        getSonReply(x, sqw, userId, sonSize);
                    });
                }
                return null;
            }, bizExecutor);



            // 查附件
            CompletableFuture<Map<String, List<BaseFileVo>>> fileF = CompletableFuture.supplyAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes, true);
                return replyFileService.getFileMap(ids);
            }, bizExecutor);

            // 查用户
            CompletableFuture<Map<String, UserInfoVo>> userF = CompletableFuture.supplyAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes, true);
                return ReplyUtil.getUserInfoVoMap4R(userClient.getUserInfoByIds(uidSet.toArray(String[]::new)));
            }, bizExecutor);

            //  点赞信息
            Map<String, LikeCountVo> likeMap = getLikeMap(ids, userId);


            Map<String, List<BaseFileVo>> fileMap =null;
            Map<String, UserInfoVo> userMap=null;

            try {
                fileMap = fileF.get(8, TimeUnit.SECONDS);
                userMap = userF.get(8, TimeUnit.SECONDS);
                sonF.get(8, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("异步获取评论出错。",e);
            }

            if (fileMap == null){
                fileMap = new HashMap<>(0);
            }
            if (userMap == null){
                userMap = new HashMap<>(0);
            }

            // 组装信息
            for (int i = 0; i < dtoPage.getRecords().size(); i++) {
                ReplyDto x = dtoPage.getRecords().get(i);
                ReplyVo vo = voPage.getRecords().get(i);
                // 附件
                vo.setFileList(fileMap.get(vo.getId()));
                // 评论人信息
                vo.setPoster(userMap.get(x.getCreatedBy()));
                if (StringUtils.isNotBlank(x.getRepostedUserId())){
                    vo.setRepostedUser(userMap.get(x.getRepostedUserId()));
                }
                // 点赞
                LikeCountVo likeCountVo = likeMap.get(x.getId());
                if (likeCountVo != null) {
                    vo.setLikeOrDis(likeCountVo.getLikeStatus());
                    vo.setLikeCount(likeCountVo.getCount());
                }
            }
        }

        return voPage;
    }



    /**
     * 分页获用户的评论
     *
     * @param page         当前页
     * @param size         分页大小
     * @param qw           查询条件
     * @return             分页结果
     */
    @Override
    public Page<ReplyDto> getUserReplyVoPage(int page, int size,String userId, QueryWrapper<ReplyIndex> qw) {


        Page<ReplyDto> dtoPage = replyIndexMapper.getDtoPage(new Page<>(page, size), qw);
        if (CollectionUtil.isNotEmpty(dtoPage.getRecords())){
            List<String> ids = new ArrayList<>(size);
            dtoPage.getRecords().forEach(x->{
                ids.add(x.getId());
            });
            // 查附件
            Map<String, List<BaseFileVo>> fileMap = replyFileService.getFileMap(ids);
            // 查是否已点赞
            Map<String, LikeCountVo> likeMap = getLikeMap(ids, userId);
            dtoPage.getRecords().forEach(x->{
                // 附件
                x.setFileList(fileMap.get(x.getId()));
                // 点赞
                LikeCountVo likeCountVo = likeMap.get(x.getId());
                if (likeCountVo != null){
                    x.setLikeOrDis(likeCountVo.getLikeStatus());
                    x.setLikeCount(likeCountVo.getCount());
                }
            });
        }
        return dtoPage;
    }




    /**
     * 获取子评论查询条件
     * @param parentId      父id
     * @param sortType      排序方式
     */
    private QueryWrapper<ReplyIndex> getQueryWrapper(String parentId,Integer sortType){

        QueryWrapper<ReplyIndex> qw = new QueryWrapper<>();
        qw.eq(StringUtils.isNotBlank(parentId),"r.root_id",parentId);
        qw.eq("r.status",SysConstant.DEFAULT_ENABLE);
        if (Objects.equals(SortEnum.SORT_LIKE.getCode(), sortType)){
            qw.orderByDesc("ri.like_count");
        }
        if (Objects.equals(SortEnum.SORT_TIME.getCode(), sortType)){
            qw.orderByDesc("r.created_date");
        }
        return qw;
    }




    /**
     * 获取子子评论查询条件
     * @param parentId      父id
     * @param sortType      排序方式
     */
    private QueryWrapper<ReplyIndex> getSonQueryWrapper(String parentId,Integer sortType){

        QueryWrapper<ReplyIndex> qw = new QueryWrapper<>();
        qw.eq(StringUtils.isNotBlank(parentId),"r.dialog",parentId);
        qw.eq("r.status",SysConstant.DEFAULT_ENABLE);
        if (Objects.equals(SortEnum.SORT_LIKE.getCode(), sortType)){
            qw.orderByDesc("ri.like_count");
        }
        if (Objects.equals(SortEnum.SORT_TIME.getCode(), sortType)){
            qw.orderByDesc("r.created_date");
        }
        return qw;
    }



    /**
     * 获取子子评论查询条件
     * @param parentId      父id
     * @param sortType      排序方式
     */
    private QueryWrapper<ReplyIndex> getLowSonQueryWrapper(String parentId,Integer sortType){

        QueryWrapper<ReplyIndex> qw = new QueryWrapper<>();
        qw.eq(StringUtils.isNotBlank(parentId),"r.parent",parentId);
        qw.eq("r.status",SysConstant.DEFAULT_ENABLE);
        if (Objects.equals(SortEnum.SORT_LIKE.getCode(), sortType)){
            qw.orderByDesc("ri.like_count");
        }
        if (Objects.equals(SortEnum.SORT_TIME.getCode(), sortType)){
            qw.orderByDesc("r.created_date");
        }
        return qw;
    }



    /**
     * 获取子评论
     * @param vo            当前评论
     * @param qw            子评论的父评论
     * @param size          取数大小
     */
    private void getSonReply(ReplyVo vo,QueryWrapper<ReplyIndex> qw,String userId,int size){

        Page<ReplyDto> dtoPage = replyIndexMapper.getDtoPage(new Page<>(1, size), qw);
        if (dtoPage.getTotal() > 0){

            Set<String> uidSet = new HashSet<>(size);
            List<String> ids = new ArrayList<>(size);

            dtoPage.getRecords().forEach(x->{
                uidSet.add(x.getCreatedBy());
                ids.add(x.getId());
                if (StringUtils.isNotBlank(x.getRepostedUserId())){
                    uidSet.add(x.getRepostedUserId());
                }
            });

            Map<String, UserInfoVo> userMap = null;
            Map<String, List<BaseFileVo>> fileMap = null;

            // 解决跨线程获取不到request对象
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

            // 查附件
            CompletableFuture<Map<String, List<BaseFileVo>>> fileF = CompletableFuture.supplyAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes, true);
                return  replyFileService.getFileMap(ids);
            }, asyncExecutor);


            // 查用户
            CompletableFuture<Map<String, UserInfoVo>> userF = CompletableFuture.supplyAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes, true);
                return  ReplyUtil.getUserInfoVoMap4R(userClient.getUserInfoByIds(uidSet.toArray(String[]::new)));
            }, asyncExecutor);

            // 点赞信息
            Map<String, LikeCountVo> likeMap = getLikeMap(ids,userId);


            try {
                userMap = userF.get();
                fileMap = fileF.get();
            }catch (Exception e){
                log.error("异步获发帖人和附件异常",e);
            }
            if (userMap == null ){
                userMap = new HashMap<>(0);
            }
            if (fileMap == null ){
                fileMap = new HashMap<>(0);
            }


            Map<String, List<BaseFileVo>> finalFileMap = fileMap;
            Map<String, UserInfoVo> finalUserMap = userMap;

            // 构造VO
            List<ReplyVo> list = dtoPage.getRecords().stream().map(x -> {
                ReplyVo sonVo = BeanCopyUtils.copyBean(x, ReplyVo.class);
                // 附件
                sonVo.setFileList(finalFileMap.get(sonVo.getId()));
                // 评论人信息
                sonVo.setPoster(finalUserMap.get(x.getCreatedBy()));
                if (StringUtils.isNotBlank(x.getRepostedUserId())) {
                    sonVo.setRepostedUser(finalUserMap.get(x.getRepostedUserId()));
                }
                // 点赞
                LikeCountVo likeCountVo = likeMap.get(x.getId());
                if (likeCountVo != null){
                    sonVo.setLikeOrDis(likeCountVo.getLikeStatus());
                    sonVo.setLikeCount(likeCountVo.getCount());
                }
                return sonVo;
            }).toList();
            vo.setSonCount(dtoPage.getTotal());
            vo.setSonList(list);
        }
    }



    /**
     * 获取是否已点赞
     */
    private Map<String, LikeCountVo> getLikeMap(List<String> ids,String userId){

        Map<String, LikeCountVo> likedMap = new HashMap<>(ids.size());

        if (StringUtils.isBlank(userId)){
            return likedMap;
        }
        // 是否已点赞
        List<LikeCountVo> liked = likeProviderService.getLikedListWithCount(BusinessObjectType.REPLY,ids,userId);
        if ( CollectionUtil.isNotEmpty(liked)){
            likedMap = liked.stream().collect(Collectors.toMap(LikeCountVo::getObjectId, Function.identity()));
        }
        return likedMap;
    }







    /**
     * 新增评论
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ReplyIndex saveReply(ReplyForm form) {

        // 获取评论区
        ReplySubject subject = replySubjectService.saveOrGetReplySubject(form);
        if (SysConstant.DEFAULT_DISABLE == subject.getStatus()){
            throw new BusinessException("禁止评论或已删除");
        }


        ReplyIndex entity = BeanCopyUtils.copyBean(form, ReplyIndex.class);
        // TODO 审核
        entity.setStatus(SysConstant.DEFAULT_ENABLE);
        entity.setSubjectId(subject.getId());


        // 上级或父级是否可评论
        List<String> pIds = new ArrayList<>(2);
        if (StringUtils.isNotBlank(form.getRootId()) && !ReplyConstant.DEFAULT_ROOT_ID.equals(form.getRootId()) ){
            pIds.add(form.getRootId());
        }
        if (StringUtils.isNotBlank(form.getParentId()) && !ReplyConstant.DEFAULT_ROOT_ID.equals(form.getParentId()) ){
            pIds.add(form.getParentId());
        }
        if (!pIds.isEmpty()){
            LambdaQueryWrapper<ReplyIndex> plqw = new LambdaQueryWrapper<>();
            plqw.eq(ReplyIndex::getStatus,SysConstant.DEFAULT_ENABLE);
            plqw.in(ReplyIndex::getId,pIds);
            long parentCount = count(plqw);
            if (parentCount < 1){
                throw new BusinessException("评论已删除或不存在");
            }
        }

        // 是否为子子评论
        if (StringUtils.isNotBlank(form.getParentId()) && !form.getParentId().equals(form.getRootId())
            && !ReplyConstant.DEFAULT_ROOT_ID.equals(form.getParentId())){
            entity.setDialog(form.getParentId());
        }

        if (StringUtils.isNotBlank(form.getRepostedUserId())){
            Result<UserDto> userInfo = userClient.getUserInfoByUsername(form.getRepostedUserId());
            if (userInfo.isFail() || userInfo.getData() == null){
                throw new BusinessException("用户信息不存在");
            }
            entity.setRepostedUserId(userInfo.getData().getId());
        }

        boolean saved = this.save(entity);
        if (!saved){
            throw new BusinessException("评论保存失败");
        }

        // 保存附件
        if (CollectionUtil.isNotEmpty(form.getFileList())) {
            List<ReplyFile> files = form.getFileList().stream().map(x -> {
                ReplyFile file = new ReplyFile();
                file.setFileId(x);
                file.setReplayId(entity.getId());
                return file;
            }).toList();
            replyFileService.saveBatch(files);
        }

        // 保存评论内容
        ReplyContent replyContent = new ReplyContent();
        replyContent.setId(entity.getId());
        replyContent.setContent(form.getContent());
        replyContentMapper.insert(replyContent);

        // 保存评论点赞数等信息
        ReplyInfo replyInfo = new ReplyInfo();
        replyInfo.setReplyCount(0);
        replyInfo.setLikeCount(0);
        replyInfo.setId(entity.getId());
        replyInfoMapper.insert(replyInfo);

        return entity;
    }



    /**
     * 更新评论状态
     */
    @Override
    public boolean updateReplyStatus(String id,Integer status){

        ReplyIndex entity = this.getById(id);
        if (entity == null || entity.getStatus().equals(status)){
            return false;
        }
        String lockKey = ReplyConstant.LOCK_REPLY_RECORD_PREFIX + id;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            entity = this.getById(id);
            if (entity == null || entity.getStatus().equals(status)){
                return false;
            }
            return doUpdateReplyStatus(id,status);
        }finally {
            lock.unlock();
        }
    }


    /**
     * 更新状态
     */
    private boolean doUpdateReplyStatus(String id,Integer status){
        LambdaUpdateWrapper<ReplyIndex> luw = new LambdaUpdateWrapper<>();
        luw.set(ReplyIndex::getStatus,status);
        luw.eq(ReplyIndex::getId,id);
        return this.update(luw);
    }



    /**
     * 删除评论
     * @param id       评论id
     * @return          删除结果
     */
    @Override
    public  boolean deleteReply(String id) {

        String lockKey = ReplyConstant.LOCK_REPLY_RECORD_PREFIX + id;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            LambdaQueryWrapper<ReplyIndex> lqw = new LambdaQueryWrapper<>();
            lqw.eq(ReplyIndex::getId,id);
            return doDelete(lqw);
        }finally {
            lock.unlock();
        }
    }




    /**
     * 用户删除自己的评论
     * @param id            评论id
     * @param userId        用户id
     * @return              删除结果
     */
    @Override
    public boolean deleteReplyByUserId(String id,String userId) {

        String lockKey = ReplyConstant.LOCK_REPLY_RECORD_PREFIX + id;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            LambdaQueryWrapper<ReplyIndex> lqw = new LambdaQueryWrapper<>();
            lqw.eq(ReplyIndex::getId,id);
            lqw.eq(ReplyIndex::getCreatedBy,userId);
            return doDelete(lqw);
        }finally {
            lock.unlock();
        }
    }


    /**
     * 删除
     */
    private boolean doDelete( LambdaQueryWrapper<ReplyIndex> lqw){
        return remove(lqw);
    }



}
